
#include "priv.h"

#define DBCS_CHARSIZE   (2)

//  N versions of wsprintf and wvsprintf which take an output buffer size to prevent overflow
//  bugs.  Taken from the NT wsprintf source code.

//  _MBToWCS and _WCSToMB are actually macros which call ntrtl functions in the NT version.
int _MBToWCS(LPSTR pszIn, int cchIn, LPWSTR *ppwszOut)
{
    int cch = 0;
    int cbAlloc;

    if ((0 != cchIn) && (NULL != ppwszOut))
    {
        cchIn++;
        cbAlloc = cchIn * sizeof(WCHAR);

        *ppwszOut = (LPWSTR)LocalAlloc(LMEM_FIXED, cbAlloc);

        if (NULL != *ppwszOut)
        {
            cch = MultiByteToWideChar(CP_ACP, 0, pszIn, cchIn, *ppwszOut, cchIn);

            if (!cch)
            {
                LocalFree(*ppwszOut);
                *ppwszOut = NULL;
            }
            else
            {
                cch--;  //  Just return the number of characters
            }
        }
    }

    return cch;
}

int _WCSToMB(LPCWSTR pwszIn, int cchIn, LPSTR *ppszOut)
{
    int cch = 0;
    int cbAlloc;

    if ((0 != cchIn) && (NULL != ppszOut))
    {
        cchIn++;
        cbAlloc = cchIn * DBCS_CHARSIZE;

        *ppszOut = (LPSTR)LocalAlloc(LMEM_FIXED, cbAlloc);

        if (NULL != *ppszOut)
        {
            cch = WideCharToMultiByte(CP_ACP, 0, pwszIn, cchIn,
                                      *ppszOut, cbAlloc, NULL, NULL);

            if (!cch)
            {
                LocalFree(*ppszOut);
                *ppszOut = NULL;
            }
            else
            {
                cch--;  //  Just return the number of characters
            }
        }
    }

    return cch;
}

/****************************** Module Header ******************************\
* Module Name: wsprintf.c
*
* Copyright (c) 1985-91, Microsoft Corporation
*  sprintf.c
*
*  Implements Windows friendly versions of sprintf and vsprintf
*
*  History:
*   2-15-89  craigc     Initial
*  11-12-90  MikeHar    Ported from windows 3
\***************************************************************************/

/* Max number of characters. Doesn't include termination character */

#define out(c) if (cchLimit) {*lpOut++=(c); cchLimit--;} else goto errorout

/***************************************************************************\
* SP_PutNumber
*
* Takes an unsigned long integer and places it into a buffer, respecting
* a buffer limit, a radix, and a case select (upper or lower, for hex).
*
*
* History:
*  11-12-90  MikeHar    Ported from windows 3 asm --> C
*  12-11-90  GregoryW   need to increment lpstr after assignment of mod
\***************************************************************************/

int SP_PutNumber(
    LPSTR lpstr,
    ULONG64 n,
    int   limit,
    DWORD radix,
    int   uppercase,
    int   *pcch)
{
    DWORD mod;
    *pcch = 0;

    /* It might not work for some locales or digit sets */
    if(uppercase)
        uppercase =  'A'-'0'-10;
    else
        uppercase = 'a'-'0'-10;

    if (limit) {
        do  {
            mod =  (ULONG)(n % radix);
            n /= radix;

            mod += '0';
            if (mod > '9')
                mod += uppercase;
            *lpstr++ = (char)mod;
            (*pcch)++;
        } while((*pcch < limit) && n);
    }

    return (n == 0) && (*pcch > 0);
}

/***************************************************************************\
* SP_Reverse
*
*  reverses a string in place
*
* History:
*  11-12-90  MikeHar    Ported from windows 3 asm --> C
*  12-11-90  GregoryW   fixed boundary conditions; removed count
\***************************************************************************/

void SP_Reverse(
    LPSTR lpFirst,
    LPSTR lpLast)
{
    char ch;

    while(lpLast > lpFirst){
        ch = *lpFirst;
        *lpFirst++ = *lpLast;
        *lpLast-- = ch;
    }
}

/***************************************************************************\
* SP_GetFmtValue
*
*  reads a width or precision value from the format string
*
* History:
*  11-12-90  MikeHar    Ported from windows 3
\***************************************************************************/

LPCSTR SP_GetFmtValue(
    LPCSTR lpch,
    int *lpw)
{
    int ii = 0;

    /* It might not work for some locales or digit sets */
    while (*lpch >= '0' && *lpch <= '9') {
        ii *= 10;
        ii += (int)(*lpch - '0');
        lpch++;
    }

    *lpw = ii;

    /*
     * return the address of the first non-digit character
     */
    return lpch;
}

/***************************************************************************\
* SP_GetFmtValueW
*
*  reads a width or precision value from the format string
*
* History:
*  11-12-90  MikeHar    Ported from windows 3
*  07-27-92  GregoryW   Created Unicode version (copied from SP_GetFmtValue)
\***************************************************************************/

LPCWSTR SP_GetFmtValueW(
    LPCWSTR lpch,
    int *lpw)
{
    int ii = 0;

    /* It might not work for some locales or digit sets */
    while (*lpch >= L'0' && *lpch <= L'9') {
        ii *= 10;
        ii += (int)(*lpch - L'0');
        lpch++;
    }

    *lpw = ii;

    /*
     * return the address of the first non-digit character
     */
    return lpch;
}

/***************************************************************************\
* wvsprintfA (API)
*
* Windows version of vsprintf().  Does not support floating point or
* pointer types, and all strings are assumed to be FAR.  Supports only
* the left alignment flag.
*
* Takes pointers to an output buffer, where the string is built, a
* pointer to an input buffer, and a pointer to a list of parameters.
*
* The cdecl function wnsprintf() calls this function.
*
* History:
* 11-12-90 MikeHar      Ported from windows 3
* 12-11-90 GregoryW     after %d format is parsed lpParms needs to be aligned
*                       to a dword boundary.
* 09-Aug-1991 mikeke    no it doesn't
* 11-19-91 DarrinM      Now wvsprintf and wsprintf treat parameters the same
*                       (as if they originated from a DWORD-aligned stack).
* 1-22-97 tnoonan       Converted to wvnsprintfA
\***************************************************************************/

// LWSTDAPI_(int) wvnsprintfA(
//     LPSTR lpOut,
//     int cchLimitIn,
//     LPCSTR lpFmt,
//     va_list arglist)
// {
//     BOOL fAllocateMem = FALSE;
//     char prefix, fillch;
//     int left, width, prec, size, sign, radix, upper, hprefix;
//     int cchLimit = --cchLimitIn, cch, cchAvailable;
//     LPSTR lpT, lpTMB = NULL;
//     LPWSTR pwsz;
//     va_list varglist = arglist;
//     union {
//         LONG64 l;
//         ULONG64 ul;
//         char sz[2];
//         WCHAR wsz[2];
//     } val;
// 
//     if (cchLimit < 0)
//         return 0;
// 
//     while (*lpFmt != 0) {
//         if (*lpFmt == '%') {
// 
//             /*
//              * read the flags.  These can be in any order
//              */
//             left = 0;
//             prefix = 0;
//             while (*++lpFmt) {
//                 if (*lpFmt == '-')
//                     left++;
//                 else if (*lpFmt == '#')
//                     prefix++;
//                 else
//                     break;
//             }
// 
//             /*
//              * find fill character
//              */
//             if (*lpFmt == '0') {
//                 fillch = '0';
//                 lpFmt++;
//             } else
//                 fillch = ' ';
// 
//             /*
//              * read the width specification
//              */
//             lpFmt = SP_GetFmtValue((LPCSTR)lpFmt, &cch);
//             width = cch;
// 
//             /*
//              * read the precision
//              */
//             if (*lpFmt == '.') {
//                 lpFmt = SP_GetFmtValue((LPCSTR)++lpFmt, &cch);
//                 prec = cch;
//             } else
//                 prec = -1;
// 
//             /*
//              * get the operand size
//              * default size: size == 0
//              * long number:  size == 1
//              * wide chars:   size == 2
//              * 64bit number: size == 3
//              * It may be a good idea to check the value of size when it
//              * is tested for non-zero below (IanJa)
//              */
//             hprefix = 0;
//             if (*lpFmt == 'w') {
//                 size = 2;
//                 lpFmt++;
//             } else if (*lpFmt == 'l') {
//                 size = 1;
//                 lpFmt++;
//             } else if (*lpFmt == 't') {
//                 size = 0;
//                 lpFmt++;
//             } else if (*lpFmt == 'I') {
//                 if (*(lpFmt+1) == '3' && *(lpFmt+2) == '2') {
//                     size = 1;
//                     lpFmt += 3;
//                 } else if (*(lpFmt+1) == '6' && *(lpFmt+2) == '4') {
//                     size = 3;
//                     lpFmt += 3;
//                 } else {
//                     size = (sizeof(INT_PTR) == sizeof(LONG)) ? 1 : 3;
//                     lpFmt++;
//                 }
//             } else {
//                 size = 0;
//                 if (*lpFmt == 'h') {
//                     lpFmt++;
//                     hprefix = 1;
//                 } else if ((*lpFmt == 'i') || (*lpFmt == 'd')) {
//                     // %i or %d specified (no modifiers) - use long
//                     // %u seems to have always been short - leave alone
//                     size = 1;
//                 }
//             }
// 
//             upper = 0;
//             sign = 0;
//             radix = 10;
// 
//             switch (*lpFmt) {
//             case 0:
//                 goto errorout;
// 
//             case 'i':
//             case 'd':
//                 sign++;
// 
//                 /*** FALL THROUGH to case 'u' ***/
// 
//             case 'u':
//                 /* turn off prefix if decimal */
//                 prefix = 0;
// donumeric:
//                 /* special cases to act like MSC v5.10 */
//                 if (left || prec >= 0)
//                     fillch = ' ';
// 
//                 /*
//                  * if size == 1, "%lu" was specified (good);
//                  * if size == 2, "%wu" was specified (bad)
//                  * if size == 3, "%p" was specified
//                  */
//                 if (size == 3) {
//                     val.l = va_arg(varglist, LONG64);
//                 } else if (size) {
//                     val.l = va_arg(varglist, long);
//                 } else if (sign) {
//                     val.l = (long)va_arg(varglist, short);
//                 } else {
//                     val.ul = va_arg(varglist, unsigned);
//                 }
// 
//                 if (sign && val.l < 0L)
//                     val.l = -val.l;
//                 else
//                     sign = 0;
// 
//                 /*
//                  * Unless printing a full 64-bit value, ensure values
//                  * here are not in canonical longword format to prevent
//                  * the sign extended upper 32-bits from being printed.
//                  */
//                 if (size != 3) {
//                     val.l &= MAXDWORD;
//                 }
// 
//                 lpT = lpOut;
// 
//                 /*
//                  * blast the number backwards into the user buffer
//                  * SP_PutNumber returns false if it runs out of space
//                  */
//                 if (!SP_PutNumber(lpOut, val.l, cchLimit, radix, upper, &cch))
//                 {
//                     break;
//                 }
// 
//                 //  Now we have the number backwards, calculate how much
//                 //  more buffer space we'll need for this number to
//                 //  format correctly.
//                 cchAvailable = cchLimit - cch;
// 
//                 width -= cch;
//                 prec -= cch;
//                 if (prec > 0)
//                 {
//                     width -= prec;
//                     cchAvailable -= prec;
//                 }
// 
//                 if (width > 0)
//                 {
//                     cchAvailable -= width - (sign ? 1 : 0);
//                 }
// 
//                 if (sign)
//                 {
//                     cchAvailable--;
//                 }
// 
//                 if (cchAvailable < 0)
//                 {
//                     break;
//                 }
// 
//                 //  We have enough space to format the buffer as requested
//                 //  without overflowing.
// 
//                 lpOut += cch;
//                 cchLimit -= cch;
// 
//                 /*
//                  * fill to the field precision
//                  */
//                 while (prec-- > 0)
//                     out('0');
// 
//                 if (width > 0 && !left) {
//                     /*
//                      * if we're filling with spaces, put sign first
//                      */
//                     if (fillch != '0') {
//                         if (sign) {
//                             sign = 0;
//                             out('-');
//                             width--;
//                         }
// 
//                         if (prefix) {
//                             out(prefix);
//                             out('0');
//                             prefix = 0;
//                         }
//                     }
// 
//                     if (sign)
//                         width--;
// 
//                     /*
//                      * fill to the field width
//                      */
//                     while (width-- > 0)
//                         out(fillch);
// 
//                     /*
//                      * still have a sign?
//                      */
//                     if (sign)
//                         out('-');
// 
//                     if (prefix) {
//                         out(prefix);
//                         out('0');
//                     }
// 
//                     /*
//                      * now reverse the string in place
//                      */
//                     SP_Reverse(lpT, lpOut - 1);
//                 } else {
//                     /*
//                      * add the sign character
//                      */
//                     if (sign) {
//                         out('-');
//                         width--;
//                     }
// 
//                     if (prefix) {
//                         out(prefix);
//                         out('0');
//                     }
// 
//                     /*
//                      * reverse the string in place
//                      */
//                     SP_Reverse(lpT, lpOut - 1);
// 
//                     /*
//                      * pad to the right of the string in case left aligned
//                      */
//                     while (width-- > 0)
//                         out(fillch);
//                 }
//                 break;
// 
//             case 'p':
//                 size = (sizeof(PVOID) == sizeof(LONG)) ? 1 : 3;
//                 if (prec == -1) {
//                     prec = 2 * sizeof(PVOID);
//                 }
// 
//                 /*** FALL THROUGH to case 'X' ***/
// 
//             case 'X':
//                 upper++;
// 
//                 /*** FALL THROUGH to case 'x' ***/
// 
//             case 'x':
//                 radix = 16;
//                 if (prefix)
//                     if (upper)
//                         prefix = 'X';
//                     else
//                         prefix = 'x';
//                 goto donumeric;
// 
//             case 'C':
//                 /*
//                  * explicit size specifier overrides case
//                  */
//                 if (!size && !hprefix) {
//                     size = 1;           // force WCHAR
//                 }
// 
//                 /*** FALL THROUGH to case 'c' ***/
// 
//             case 'c':
//                 /*
//                  * if size == 0, "%c" or "%hc" or "%tc" was specified (CHAR)
//                  * if size == 1, "%C" or "%lc" was specified (WCHAR);
//                  * if size == 2, "%wc" was specified (WCHAR)
//                  */
//                 cch = 1; /* One character must be copied to the output buffer */
//                 if (size) {
//                     val.wsz[0] = va_arg(varglist, WCHAR);
//                     val.wsz[1] = 0x0000;
//                     pwsz = val.wsz;
//                     goto putwstring;
//                 } else {
//                     val.sz[0] = va_arg(varglist, CHAR);
//                     val.sz[1] = 0;
//                     lpT = val.sz;
//                     goto putstring;
//                 }
// 
//             case 'S':
//                 /*
//                  * explicit size specifier overrides case
//                  */
//                 if (!size && !hprefix) {
//                     size = 1;           // force LPWSTR
//                 }
// 
//                 /*** FALL THROUGH to case 's' ***/
// 
//             case 's':
//                 /*
//                  * if size == 0, "%s" or "%hs" or "%ts" was specified (LPSTR);
//                  * if size == 1, "%S" or "%ls" was specified (LPWSTR);
//                  * if size == 2, "%ws" was specified (LPWSTR)
//                  */
//                 if (size) {
//                     pwsz = va_arg(varglist, LPWSTR);
//                     if (pwsz == NULL) {
//                         cch = 0;
//                     } else {
//                         cch = wcslen(pwsz);
//                     }
// putwstring:
//                     cch = _WCSToMB(pwsz, cch, &lpTMB);
//                     fAllocateMem = (BOOL) cch;
//                     lpT = lpTMB;
//                 } else {
//                     lpT = va_arg(varglist, LPSTR);
//                     if (lpT == NULL) {
//                         cch = 0;
//                     } else {
//                         cch = strlen(lpT);
//                     }
//                 }
// putstring:
//                 if (prec >= 0 && cch > prec)
//                     cch = prec;
//                 width -= cch;
// 
//                 if (left) {
//                     while (cch--)
//                         out(*lpT++);
//                     while (width-- > 0)
//                         out(fillch);
//                 } else {
//                     while (width-- > 0)
//                         out(fillch);
//                     while (cch--)
//                         out(*lpT++);
//                 }
// 
//                 if (fAllocateMem) {
//                      LocalFree(lpTMB);
//                      fAllocateMem = FALSE;
//                 }
//                 break;
// 
//             default:
// normalch:
//                 if (IsDBCSLeadByte(*lpFmt)) {
//                     out(*lpFmt++);
//                     if (!*lpFmt)
//                         break;      // lead byte with no trail byte
//                 }
//                 out(*lpFmt);
//                 break;
//             }  /* END OF SWITCH(*lpFmt) */
//         }  /* END OF IF(%) */ else
//             goto normalch;  /* character not a '%', just do it */
// 
//         /*
//          * advance to next format string character
//          */
//         lpFmt++;
//     }  /* END OF OUTER WHILE LOOP */
// 
// errorout:
//     *lpOut = 0;
// 
//     if (fAllocateMem)
//     {
//         LocalFree(lpTMB);
//     }
// 
//     return cchLimitIn - cchLimit;
// }

/***************************************************************************\
* StringPrintfA (API)
*
* Windows version of sprintf
*
* History:
* 11-12-90 MikeHar      Ported from windows 3
* 02-05-90 DarrinM      Cleaned up with STDARG.h vararg stuff.
* 1-22-97 tnoonan       Converted to wnsprintfA
\***************************************************************************/

// LWSTDAPIV_(int) wnsprintfA(
//     LPSTR lpOut,
//     int cchLimitIn,
//     LPCSTR lpFmt,
//     ...)
// {
//     va_list arglist;
//     int ret;
// 
//     va_start(arglist, lpFmt);
//     ret = wvnsprintfA(lpOut, cchLimitIn, lpFmt, arglist);
//     va_end(arglist);
//     return ret;
// }

/***************************************************************************\
* SP_PutNumberW
*
* Takes an unsigned long integer and places it into a buffer, respecting
* a buffer limit, a radix, and a case select (upper or lower, for hex).
*
*
* History:
*  11-12-90  MikeHar    Ported from windows 3 asm --> C
*  12-11-90  GregoryW   need to increment lpstr after assignment of mod
*  02-11-92  GregoryW   temporary version until we have C runtime support
\***************************************************************************/

int SP_PutNumberW(
    LPWSTR lpstr,
    ULONG64 n,
    int   limit,
    DWORD radix,
    int   uppercase,
    int   *pcch)
{
    DWORD mod;
    *pcch = 0;

    /* It might not work for some locales or digit sets */
    if(uppercase)
        uppercase =  'A'-'0'-10;
    else
        uppercase = 'a'-'0'-10;

    if (limit) {
        do  {
            mod =  (ULONG)(n % radix);
            n /= radix;

            mod += '0';
            if (mod > '9')
            mod += uppercase;
            *lpstr++ = (WCHAR)mod;
            (*pcch)++;
        } while((*pcch < limit) && n);
    }

    return (n == 0) && (*pcch > 0);
}

/***************************************************************************\
* SP_ReverseW
*
*  reverses a string in place
*
* History:
*  11-12-90  MikeHar    Ported from windows 3 asm --> C
*  12-11-90  GregoryW   fixed boundary conditions; removed count
*  02-11-92  GregoryW   temporary version until we have C runtime support
\***************************************************************************/

void SP_ReverseW(
    LPWSTR lpFirst,
    LPWSTR lpLast)
{
    WCHAR ch;

    while(lpLast > lpFirst){
        ch = *lpFirst;
        *lpFirst++ = *lpLast;
        *lpLast-- = ch;
    }
}


/***************************************************************************\
* wvsprintfW (API)
*
* wsprintfW() calls this function.
*
* History:
*    11-Feb-1992 GregoryW copied xwvsprintf
*         Temporary hack until we have C runtime support
* 1-22-97 tnoonan       Converted to wvnsprintfW
\***************************************************************************/

// LWSTDAPI_(int) wvnsprintfW(
//     LPWSTR lpOut,
//     int cchLimitIn,
//     LPCWSTR lpFmt,
//     va_list arglist)
// {
//     BOOL fAllocateMem = FALSE;
//     WCHAR prefix, fillch;
//     int left, width, prec, size, sign, radix, upper, hprefix;
//     int cchLimit = --cchLimitIn, cch, cchAvailable;
//     LPWSTR lpT, lpTWC = NULL;
//     LPBYTE psz;
//     va_list varglist = arglist;
//     union {
//         LONG64 l;
//         ULONG64 ul;
//         char sz[2];
//         WCHAR wsz[2];
//     } val;
// 
//     if (cchLimit < 0)
//         return 0;
// 
//     while (*lpFmt != 0) {
//         if (*lpFmt == L'%') {
// 
//             /*
//              * read the flags.  These can be in any order
//              */
//             left = 0;
//             prefix = 0;
//             while (*++lpFmt) {
//                 if (*lpFmt == L'-')
//                     left++;
//                 else if (*lpFmt == L'#')
//                     prefix++;
//                 else
//                     break;
//             }
// 
//             /*
//              * find fill character
//              */
//             if (*lpFmt == L'0') {
//                 fillch = L'0';
//                 lpFmt++;
//             } else
//                 fillch = L' ';
// 
//             /*
//              * read the width specification
//              */
//             lpFmt = SP_GetFmtValueW(lpFmt, &cch);
//             width = cch;
// 
//             /*
//              * read the precision
//              */
//             if (*lpFmt == L'.') {
//                 lpFmt = SP_GetFmtValueW(++lpFmt, &cch);
//                 prec = cch;
//             } else
//                 prec = -1;
// 
//             /*
//              * get the operand size
//              * default size: size == 0
//              * long number:  size == 1
//              * wide chars:   size == 2
//              * 64bit number: size == 3
//              * It may be a good idea to check the value of size when it
//              * is tested for non-zero below (IanJa)
//              */
//             hprefix = 0;
//             if ((*lpFmt == L'w') || (*lpFmt == L't')) {
//                 size = 2;
//                 lpFmt++;
//             } else if (*lpFmt == L'l') {
//                 size = 1;
//                 lpFmt++;
//             } else if (*lpFmt == L'I') {
//                 if (*(lpFmt+1) == L'3' && *(lpFmt+2) == L'2') {
//                     size = 1;
//                     lpFmt += 3;
//                 } else if (*(lpFmt+1) == L'6' && *(lpFmt+2) == L'4') {
//                     size = 3;
//                     lpFmt += 3;
//                 } else {
//                     size = (sizeof(INT_PTR) == sizeof(LONG)) ? 1 : 3;
//                     lpFmt++;
//                 }
//             } else {
//                 size = 0;
//                 if (*lpFmt == L'h') {
//                     lpFmt++;
//                     hprefix = 1;
//                 } else if ((*lpFmt == L'i') || (*lpFmt == L'd')) {
//                     // %i or %d specified (no modifiers) - use long
//                     // %u seems to have always been short - leave alone
//                     size = 1;
//                 }
//             }
// 
//             upper = 0;
//             sign = 0;
//             radix = 10;
// 
//             switch (*lpFmt) {
//             case 0:
//                 goto errorout;
// 
//             case L'i':
//             case L'd':
//                 sign++;
// 
//                 /*** FALL THROUGH to case 'u' ***/
// 
//             case L'u':
//                 /* turn off prefix if decimal */
//                 prefix = 0;
// donumeric:
//                 /* special cases to act like MSC v5.10 */
//                 if (left || prec >= 0)
//                     fillch = L' ';
// 
//                 /*
//                  * if size == 1, "%lu" was specified (good);
//                  * if size == 2, "%wu" was specified (bad)
//                  * if size == 3, "%p" was specified
//                  */
//                 if (size == 3) {
//                     val.l = va_arg(varglist, LONG64);
//                 } else if (size) {
//                     val.l = va_arg(varglist, LONG);
//                 } else if (sign) {
//                     val.l = va_arg(varglist, SHORT);
//                 } else {
//                     val.ul = va_arg(varglist, unsigned);
//                 }
// 
//                 if (sign && val.l < 0L)
//                     val.l = -val.l;
//                 else
//                     sign = 0;
// 
//                 /*
//                  * Unless printing a full 64-bit value, ensure values
//                  * here are not in canonical longword format to prevent
//                  * the sign extended upper 32-bits from being printed.
//                  */
//                 if (size != 3) {
//                     val.l &= MAXDWORD;
//                 }
// 
//                 lpT = lpOut;
// 
//                 /*
//                  * blast the number backwards into the user buffer
//                  * SP_PutNumberW returns FALSE if it runs out of space
//                  */
//                 if (!SP_PutNumberW(lpOut, val.l, cchLimit, radix, upper, &cch))
//                 {
//                     break;
//                 }
// 
//                 //  Now we have the number backwards, calculate how much
//                 //  more buffer space we'll need for this number to
//                 //  format correctly.
//                 cchAvailable = cchLimit - cch;
// 
//                 width -= cch;
//                 prec -= cch;
//                 if (prec > 0)
//                 {
//                     width -= prec;
//                     cchAvailable -= prec;
//                 }
// 
//                 if (width > 0)
//                 {
//                     cchAvailable -= width - (sign ? 1 : 0);
//                 }
// 
//                 if (sign)
//                 {
//                     cchAvailable--;
//                 }
// 
//                 if (cchAvailable < 0)
//                 {
//                     break;
//                 }
// 
//                 //  We have enough space to format the buffer as requested
//                 //  without overflowing.
// 
//                 lpOut += cch;
//                 cchLimit -= cch;
// 
//                 /*
//                  * fill to the field precision
//                  */
//                 while (prec-- > 0)
//                     out(L'0');
// 
//                 if (width > 0 && !left) {
//                     /*
//                      * if we're filling with spaces, put sign first
//                      */
//                     if (fillch != L'0') {
//                         if (sign) {
//                             sign = 0;
//                             out(L'-');
//                             width--;
//                         }
// 
//                         if (prefix) {
//                             out(prefix);
//                             out(L'0');
//                             prefix = 0;
//                         }
//                     }
// 
//                     if (sign)
//                         width--;
// 
//                     /*
//                      * fill to the field width
//                      */
//                     while (width-- > 0)
//                         out(fillch);
// 
//                     /*
//                      * still have a sign?
//                      */
//                     if (sign)
//                         out(L'-');
// 
//                     if (prefix) {
//                         out(prefix);
//                         out(L'0');
//                     }
// 
//                     /*
//                      * now reverse the string in place
//                      */
//                     SP_ReverseW(lpT, lpOut - 1);
//                 } else {
//                     /*
//                      * add the sign character
//                      */
//                     if (sign) {
//                         out(L'-');
//                         width--;
//                     }
// 
//                     if (prefix) {
//                         out(prefix);
//                         out(L'0');
//                     }
// 
//                     /*
//                      * reverse the string in place
//                      */
//                     SP_ReverseW(lpT, lpOut - 1);
// 
//                     /*
//                      * pad to the right of the string in case left aligned
//                      */
//                     while (width-- > 0)
//                         out(fillch);
//                 }
//                 break;
// 
//             case L'p':
//                 size = (sizeof(PVOID) == sizeof(LONG)) ? 1 : 3;
//                 if (prec == -1) {
//                     prec = 2 * sizeof(PVOID);
//                 }
// 
//                 /*** FALL THROUGH to case 'X' ***/
// 
//             case L'X':
//                 upper++;
// 
//                 /*** FALL THROUGH to case 'x' ***/
// 
//             case L'x':
//                 radix = 16;
//                 if (prefix)
//                     if (upper)
//                         prefix = L'X';
//                     else
//                         prefix = L'x';
//                 goto donumeric;
// 
//             case L'c':
//                 if (!size && !hprefix) {
//                     size = 1;           // force WCHAR
//                 }
// 
//                 /*** FALL THROUGH to case 'C' ***/
// 
//             case L'C':
//                 /*
//                  * if size == 0, "%C" or "%hc" was specified (CHAR);
//                  * if size == 1, "%c" or "%lc" was specified (WCHAR);
//                  * if size == 2, "%wc" or "%tc" was specified (WCHAR)
//                  */
//                 cch = 1; /* One character must be copied to the output buffer */
//                 if (size) {
//                     val.wsz[0] = va_arg(varglist, WCHAR);
//                     val.wsz[1] = 0;
//                     lpT = val.wsz;
//                     goto putwstring;
//                 } else {
//                     val.sz[0] = va_arg(varglist, CHAR);
//                     val.sz[1] = 0;
//                     psz = val.sz;
//                     goto putstring;
//                 }
// 
//             case L's':
//                 if (!size && !hprefix) {
//                     size = 1;           // force LPWSTR
//                 }
// 
//                 /*** FALL THROUGH to case 'S' ***/
// 
//             case L'S':
//                 /*
//                  * if size == 0, "%S" or "%hs" was specified (LPSTR)
//                  * if size == 1, "%s" or "%ls" was specified (LPWSTR);
//                  * if size == 2, "%ws" or "%ts" was specified (LPWSTR)
//                  */
//                 if (size) {
//                     lpT = va_arg(varglist, LPWSTR);
//                     if (lpT == NULL) {
//                         cch = 0;
//                     } else {
//                         cch = wcslen(lpT);
//                     }
//                 } else {
//                     psz = va_arg(varglist, LPBYTE);
//                     if (psz == NULL) {
//                         cch = 0;
//                     } else {
//                         cch = strlen(psz);
//                     }
// putstring:
//                     cch = _MBToWCS(psz, cch, &lpTWC);
//                     fAllocateMem = (BOOL) cch;
//                     lpT = lpTWC;
//                 }
// putwstring:
//                 if (prec >= 0 && cch > prec)
//                     cch = prec;
//                 width -= cch;
// 
//                 if (left) {
//                     while (cch--)
//                         out(*lpT++);
//                     while (width-- > 0)
//                         out(fillch);
//                 } else {
//                     while (width-- > 0)
//                         out(fillch);
//                     while (cch--)
//                         out(*lpT++);
//                 }
// 
//                 if (fAllocateMem) {
//                      LocalFree(lpTWC);
//                      fAllocateMem = FALSE;
//                 }
// 
//                 break;
// 
//             default:
// normalch:
//                 out((WCHAR)*lpFmt);
//                 break;
//             }  /* END OF SWITCH(*lpFmt) */
//         }  /* END OF IF(%) */ else
//             goto normalch;  /* character not a '%', just do it */
// 
//         /*
//          * advance to next format string character
//          */
//         lpFmt++;
//     }  /* END OF OUTER WHILE LOOP */
// 
// errorout:
//     *lpOut = 0;
// 
//     if (fAllocateMem)
//     {
//         LocalFree(lpTWC);
//     }
// 
//     return cchLimitIn - cchLimit;
// }

// LWSTDAPIV_(int) wnsprintfW(
//     LPWSTR lpOut,
//     int cchLimitIn,
//     LPCWSTR lpFmt,
//     ...)
// {
//     va_list arglist;
//     int ret;
// 
//     va_start(arglist, lpFmt);
//     ret = wvnsprintfW(lpOut, cchLimitIn, lpFmt, arglist);
//     va_end(arglist);
//     return ret;
// }
